﻿using Ninject;
using PAP.Application.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PAP.Services.Commands
{
    public sealed class CommandDispatcher : ICommandDispatcher
    {
        #region "Fields"
        private readonly IUnitOfWork _unitOfWork;
        private readonly IKernel _kernel;
        #endregion

        public CommandDispatcher(IUnitOfWork unitOfWork, IKernel kernel)
        {
            this._unitOfWork = unitOfWork;
            this._kernel = kernel;
        }

        public TResponse Execute<TCommand, TResponse>(TCommand request, bool asynchronously = false)
            where TCommand : ICommand, new()
            where TResponse : IResponse, new()
        {
            if (asynchronously)
            {
                Task.Factory.StartNew(() => Execute<TCommand, TResponse>(request, false));
            }

            Type openType = typeof(ICommandHandler<,>);
            var type = openType.MakeGenericType(typeof(TCommand), typeof(TResponse));
            var requestHandler = _kernel.Get(type);
            var methodInfo = type.GetMethod("Handle");
            try
            {
                return (TResponse)methodInfo.Invoke(requestHandler, new[] { (object)request });
            }
            catch (Exception ex)
            {
                throw ex.InnerException;
            }
        }

        public void Execute<TCommand>(TCommand request, bool asynchronously= false) 
            where TCommand : ICommand, new()
        {
            if (asynchronously)
            {
                Task.Factory.StartNew(() => Execute<TCommand>(request, false));
            }

            Type openType = typeof(ICommandHandler<>);
            var type = openType.MakeGenericType(typeof(TCommand));
            var requestHandler = _kernel.Get(type);
            var methodInfo = type.GetMethod("Handle");
            try
            {
                methodInfo.Invoke(requestHandler, new[] { (object)request });
            }
            catch (Exception ex)
            {
                throw ex.InnerException;
            }
        }
    }
}
